home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / inetutil.1 / inetutil / inetutils-1.1 / libtelnet / auth.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-22  |  15.2 KB  |  675 lines

  1. /*-
  2.  * Copyright (c) 1991, 1993
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char sccsid[] = "@(#)auth.c    8.3 (Berkeley) 5/30/95";
  36. #endif /* not lint */
  37.  
  38. /*
  39.  * Copyright (C) 1990 by the Massachusetts Institute of Technology
  40.  *
  41.  * Export of this software from the United States of America is assumed
  42.  * to require a specific license from the United States Government.
  43.  * It is the responsibility of any person or organization contemplating
  44.  * export to obtain such a license before exporting.
  45.  *
  46.  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
  47.  * distribute this software and its documentation for any purpose and
  48.  * without fee is hereby granted, provided that the above copyright
  49.  * notice appear in all copies and that both that copyright notice and
  50.  * this permission notice appear in supporting documentation, and that
  51.  * the name of M.I.T. not be used in advertising or publicity pertaining
  52.  * to distribution of the software without specific, written prior
  53.  * permission.  M.I.T. makes no representations about the suitability of
  54.  * this software for any purpose.  It is provided "as is" without express
  55.  * or implied warranty.
  56.  */
  57.  
  58. #ifdef HAVE_CONFIG_H
  59. #include <config.h>
  60. #endif
  61.  
  62. #if    defined(AUTHENTICATION)
  63. #include <stdio.h>
  64. #include <sys/types.h>
  65. #include <signal.h>
  66. #define    AUTH_NAMES
  67. #include <arpa/telnet.h>
  68. #ifdef    __STDC__
  69. #include <stdlib.h>
  70. #endif
  71. #ifdef    NO_STRING_H
  72. #include <strings.h>
  73. #else
  74. #include <string.h>
  75. #endif
  76.  
  77. #include "encrypt.h"
  78. #include "auth.h"
  79. #include "misc-proto.h"
  80. #include "auth-proto.h"
  81.  
  82. #define    typemask(x)        (1<<((x)-1))
  83.  
  84. #ifdef    KRB4_ENCPWD
  85. extern krb4encpwd_init();
  86. extern krb4encpwd_send();
  87. extern krb4encpwd_is();
  88. extern krb4encpwd_reply();
  89. extern krb4encpwd_status();
  90. extern krb4encpwd_printsub();
  91. #endif
  92.  
  93. #ifdef    RSA_ENCPWD
  94. extern rsaencpwd_init();
  95. extern rsaencpwd_send();
  96. extern rsaencpwd_is();
  97. extern rsaencpwd_reply();
  98. extern rsaencpwd_status();
  99. extern rsaencpwd_printsub();
  100. #endif
  101.  
  102. int auth_debug_mode = 0;
  103. static     char    *Name = "Noname";
  104. static    int    Server = 0;
  105. static    Authenticator    *authenticated = 0;
  106. static    int    authenticating = 0;
  107. static    int    validuser = 0;
  108. static    unsigned char    _auth_send_data[256];
  109. static    unsigned char    *auth_send_data;
  110. static    int    auth_send_cnt = 0;
  111.  
  112. /*
  113.  * Authentication types supported.  Plese note that these are stored
  114.  * in priority order, i.e. try the first one first.
  115.  */
  116. Authenticator authenticators[] = {
  117. #ifdef    SPX
  118.     { AUTHTYPE_SPX, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
  119.                 spx_init,
  120.                 spx_send,
  121.                 spx_is,
  122.                 spx_reply,
  123.                 spx_status,
  124.                 spx_printsub },
  125.     { AUTHTYPE_SPX, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
  126.                 spx_init,
  127.                 spx_send,
  128.                 spx_is,
  129.                 spx_reply,
  130.                 spx_status,
  131.                 spx_printsub },
  132. #endif
  133. #ifdef    KRB5
  134. # ifdef    ENCRYPTION
  135.     { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
  136.                 kerberos5_init,
  137.                 kerberos5_send,
  138.                 kerberos5_is,
  139.                 kerberos5_reply,
  140.                 kerberos5_status,
  141.                 kerberos5_printsub },
  142. # endif    /* ENCRYPTION */
  143.     { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
  144.                 kerberos5_init,
  145.                 kerberos5_send,
  146.                 kerberos5_is,
  147.                 kerberos5_reply,
  148.                 kerberos5_status,
  149.                 kerberos5_printsub },
  150. #endif
  151. #ifdef    KRB4
  152. # ifdef ENCRYPTION
  153.     { AUTHTYPE_KERBEROS_V4, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
  154.                 kerberos4_init,
  155.                 kerberos4_send,
  156.                 kerberos4_is,
  157.                 kerberos4_reply,
  158.                 kerberos4_status,
  159.                 kerberos4_printsub },
  160. # endif    /* ENCRYPTION */
  161.     { AUTHTYPE_KERBEROS_V4, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
  162.                 kerberos4_init,
  163.                 kerberos4_send,
  164.                 kerberos4_is,
  165.                 kerberos4_reply,
  166.                 kerberos4_status,
  167.                 kerberos4_printsub },
  168. #endif
  169. #ifdef    KRB4_ENCPWD
  170.     { AUTHTYPE_KRB4_ENCPWD, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
  171.                 krb4encpwd_init,
  172.                 krb4encpwd_send,
  173.                 krb4encpwd_is,
  174.                 krb4encpwd_reply,
  175.                 krb4encpwd_status,
  176.                 krb4encpwd_printsub },
  177. #endif
  178. #ifdef    RSA_ENCPWD
  179.     { AUTHTYPE_RSA_ENCPWD, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
  180.                 rsaencpwd_init,
  181.                 rsaencpwd_send,
  182.                 rsaencpwd_is,
  183.                 rsaencpwd_reply,
  184.                 rsaencpwd_status,
  185.                 rsaencpwd_printsub },
  186. #endif
  187.     { 0, },
  188. };
  189.  
  190. static Authenticator NoAuth = { 0 };
  191.  
  192. static int    i_support = 0;
  193. static int    i_wont_support = 0;
  194.  
  195.     Authenticator *
  196. findauthenticator(type, way)
  197.     int type;
  198.     int way;
  199. {
  200.     Authenticator *ap = authenticators;
  201.  
  202.     while (ap->type && (ap->type != type || ap->way != way))
  203.         ++ap;
  204.     return(ap->type ? ap : 0);
  205. }
  206.  
  207.     void
  208. auth_init(name, server)
  209.     char *name;
  210.     int server;
  211. {
  212.     Authenticator *ap = authenticators;
  213.  
  214.     Server = server;
  215.     Name = name;
  216.  
  217.     i_support = 0;
  218.     authenticated = 0;
  219.     authenticating = 0;
  220.     while (ap->type) {
  221.         if (!ap->init || (*ap->init)(ap, server)) {
  222.             i_support |= typemask(ap->type);
  223.             if (auth_debug_mode)
  224.                 printf(">>>%s: I support auth type %d %d\r\n",
  225.                     Name,
  226.                     ap->type, ap->way);
  227.         }
  228.         else if (auth_debug_mode)
  229.             printf(">>>%s: Init failed: auth type %d %d\r\n",
  230.                 Name, ap->type, ap->way);
  231.         ++ap;
  232.     }
  233. }
  234.  
  235.     void
  236. auth_disable_name(name)
  237.     char *name;
  238. {
  239.     int x;
  240.     for (x = 0; x < AUTHTYPE_CNT; ++x) {
  241.         if (!strcasecmp(name, AUTHTYPE_NAME(x))) {
  242.             i_wont_support |= typemask(x);
  243.             break;
  244.         }
  245.     }
  246. }
  247.  
  248.     int
  249. getauthmask(type, maskp)
  250.     char *type;
  251.     int *maskp;
  252. {
  253.     register int x;
  254.  
  255.     if (!strcasecmp(type, AUTHTYPE_NAME(0))) {
  256.         *maskp = -1;
  257.         return(1);
  258.     }
  259.  
  260.     for (x = 1; x < AUTHTYPE_CNT; ++x) {
  261.         if (!strcasecmp(type, AUTHTYPE_NAME(x))) {
  262.             *maskp = typemask(x);
  263.             return(1);
  264.         }
  265.     }
  266.     return(0);
  267. }
  268.  
  269.     int
  270. auth_enable(type)
  271.     char *type;
  272. {
  273.     return(auth_onoff(type, 1));
  274. }
  275.  
  276.     int
  277. auth_disable(type)
  278.     char *type;
  279. {
  280.     return(auth_onoff(type, 0));
  281. }
  282.  
  283.     int
  284. auth_onoff(type, on)
  285.     char *type;
  286.     int on;
  287. {
  288.     int i, mask = -1;
  289.     Authenticator *ap;
  290.  
  291.     if (!strcasecmp(type, "?") || !strcasecmp(type, "help")) {
  292.                 printf("auth %s 'type'\n", on ? "enable" : "disable");
  293.         printf("Where 'type' is one of:\n");
  294.         printf("\t%s\n", AUTHTYPE_NAME(0));
  295.         mask = 0;
  296.         for (ap = authenticators; ap->type; ap++) {
  297.             if ((mask & (i = typemask(ap->type))) != 0)
  298.                 continue;
  299.             mask |= i;
  300.             printf("\t%s\n", AUTHTYPE_NAME(ap->type));
  301.         }
  302.         return(0);
  303.     }
  304.  
  305.     if (!getauthmask(type, &mask)) {
  306.         printf("%s: invalid authentication type\n", type);
  307.         return(0);
  308.     }
  309.     if (on)
  310.         i_wont_support &= ~mask;
  311.     else
  312.         i_wont_support |= mask;
  313.     return(1);
  314. }
  315.  
  316.     int
  317. auth_togdebug(on)
  318.     int on;
  319. {
  320.     if (on < 0)
  321.         auth_debug_mode ^= 1;
  322.     else
  323.         auth_debug_mode = on;
  324.     printf("auth debugging %s\n", auth_debug_mode ? "enabled" : "disabled");
  325.     return(1);
  326. }
  327.  
  328.     int
  329. auth_status()
  330. {
  331.     Authenticator *ap;
  332.     int i, mask;
  333.  
  334.     if (i_wont_support == -1)
  335.         printf("Authentication disabled\n");
  336.     else
  337.         printf("Authentication enabled\n");
  338.  
  339.     mask = 0;
  340.     for (ap = authenticators; ap->type; ap++) {
  341.         if ((mask & (i = typemask(ap->type))) != 0)
  342.             continue;
  343.         mask |= i;
  344.         printf("%s: %s\n", AUTHTYPE_NAME(ap->type),
  345.             (i_wont_support & typemask(ap->type)) ?
  346.                     "disabled" : "enabled");
  347.     }
  348.     return(1);
  349. }
  350.  
  351. /*
  352.  * This routine is called by the server to start authentication
  353.  * negotiation.
  354.  */
  355.     void
  356. auth_request()
  357. {
  358.     static unsigned char str_request[64] = { IAC, SB,
  359.                          TELOPT_AUTHENTICATION,
  360.                          TELQUAL_SEND, };
  361.     Authenticator *ap = authenticators;
  362.     unsigned char *e = str_request + 4;
  363.  
  364.     if (!authenticating) {
  365.         authenticating = 1;
  366.         while (ap->type) {
  367.             if (i_support & ~i_wont_support & typemask(ap->type)) {
  368.                 if (auth_debug_mode) {
  369.                     printf(">>>%s: Sending type %d %d\r\n",
  370.                         Name, ap->type, ap->way);
  371.                 }
  372.                 *e++ = ap->type;
  373.                 *e++ = ap->way;
  374.             }
  375.             ++ap;
  376.         }
  377.         *e++ = IAC;
  378.         *e++ = SE;
  379.         net_write(str_request, e - str_request);
  380.         printsub('>', &str_request[2], e - str_request - 2);
  381.     }
  382. }
  383.  
  384. /*
  385.  * This is called when an AUTH SEND is received.
  386.  * It should never arrive on the server side (as only the server can
  387.  * send an AUTH SEND).
  388.  * You should probably respond to it if you can...
  389.  *
  390.  * If you want to respond to the types out of order (i.e. even
  391.  * if he sends  LOGIN KERBEROS and you support both, you respond
  392.  * with KERBEROS instead of LOGIN (which is against what the
  393.  * protocol says)) you will have to hack this code...
  394.  */
  395.     void
  396. auth_send(data, cnt)
  397.     unsigned char *data;
  398.     int cnt;
  399. {
  400.     Authenticator *ap;
  401.     static unsigned char str_none[] = { IAC, SB, TELOPT_AUTHENTICATION,
  402.                         TELQUAL_IS, AUTHTYPE_NULL, 0,
  403.                         IAC, SE };
  404.     if (Server) {
  405.         if (auth_debug_mode) {
  406.             printf(">>>%s: auth_send called!\r\n", Name);
  407.         }
  408.         return;
  409.     }
  410.  
  411.     if (auth_debug_mode) {
  412.         printf(">>>%s: auth_send got:", Name);
  413.         printd(data, cnt); printf("\r\n");
  414.     }
  415.  
  416.     /*
  417.      * Save the data, if it is new, so that we can continue looking
  418.      * at it if the authorization we try doesn't work
  419.      */
  420.     if (data < _auth_send_data ||
  421.         data > _auth_send_data + sizeof(_auth_send_data)) {
  422.         auth_send_cnt = cnt > sizeof(_auth_send_data)
  423.                     ? sizeof(_auth_send_data)
  424.                     : cnt;
  425.         memmove((void *)_auth_send_data, (void *)data, auth_send_cnt);
  426.         auth_send_data = _auth_send_data;
  427.     } else {
  428.         /*
  429.          * This is probably a no-op, but we just make sure
  430.          */
  431.         auth_send_data = data;
  432.         auth_send_cnt = cnt;
  433.     }
  434.     while ((auth_send_cnt -= 2) >= 0) {
  435.         if (auth_debug_mode)
  436.             printf(">>>%s: He supports %d\r\n",
  437.                 Name, *auth_send_data);
  438.         if ((i_support & ~i_wont_support) & typemask(*auth_send_data)) {
  439.             ap = findauthenticator(auth_send_data[0],
  440.                            auth_send_data[1]);
  441.             if (ap && ap->send) {
  442.                 if (auth_debug_mode)
  443.                     printf(">>>%s: Trying %d %d\r\n",
  444.                         Name, auth_send_data[0],
  445.                             auth_send_data[1]);
  446.                 if ((*ap->send)(ap)) {
  447.                     /*
  448.                      * Okay, we found one we like
  449.                      * and did it.
  450.                      * we can go home now.
  451.                      */
  452.                     if (auth_debug_mode)
  453.                         printf(">>>%s: Using type %d\r\n",
  454.                             Name, *auth_send_data);
  455.                     auth_send_data += 2;
  456.                     return;
  457.                 }
  458.             }
  459.             /* else
  460.              *    just continue on and look for the
  461.              *    next one if we didn't do anything.
  462.              */
  463.         }
  464.         auth_send_data += 2;
  465.     }
  466.     net_write(str_none, sizeof(str_none));
  467.     printsub('>', &str_none[2], sizeof(str_none) - 2);
  468.     if (auth_debug_mode)
  469.         printf(">>>%s: Sent failure message\r\n", Name);
  470.     auth_finished(0, AUTH_REJECT);
  471. #ifdef KANNAN
  472.     /*
  473.      *  We requested strong authentication, however no mechanisms worked.
  474.      *  Therefore, exit on client end.
  475.      */
  476.     printf("Unable to securely authenticate user ... exit\n"); 
  477.     exit(0);
  478. #endif /* KANNAN */
  479. }
  480.  
  481.     void
  482. auth_send_retry()
  483. {
  484.     /*
  485.      * if auth_send_cnt <= 0 then auth_send will end up rejecting
  486.      * the authentication and informing the other side of this.
  487.      */
  488.     auth_send(auth_send_data, auth_send_cnt);
  489. }
  490.  
  491.     void
  492. auth_is(data, cnt)
  493.     unsigned char *data;
  494.     int cnt;
  495. {
  496.     Authenticator *ap;
  497.  
  498.     if (cnt < 2)
  499.         return;
  500.  
  501.     if (data[0] == AUTHTYPE_NULL) {
  502.         auth_finished(0, AUTH_REJECT);
  503.         return;
  504.     }
  505.  
  506.     if (ap = findauthenticator(data[0], data[1])) {
  507.         if (ap->is)
  508.             (*ap->is)(ap, data+2, cnt-2);
  509.     } else if (auth_debug_mode)
  510.         printf(">>>%s: Invalid authentication in IS: %d\r\n",
  511.             Name, *data);
  512. }
  513.  
  514.     void
  515. auth_reply(data, cnt)
  516.     unsigned char *data;
  517.     int cnt;
  518. {
  519.     Authenticator *ap;
  520.  
  521.     if (cnt < 2)
  522.         return;
  523.  
  524.     if (ap = findauthenticator(data[0], data[1])) {
  525.         if (ap->reply)
  526.             (*ap->reply)(ap, data+2, cnt-2);
  527.     } else if (auth_debug_mode)
  528.         printf(">>>%s: Invalid authentication in SEND: %d\r\n",
  529.             Name, *data);
  530. }
  531.  
  532.     void
  533. auth_name(data, cnt)
  534.     unsigned char *data;
  535.     int cnt;
  536. {
  537.     Authenticator *ap;
  538.     unsigned char savename[256];
  539.  
  540.     if (cnt < 1) {
  541.         if (auth_debug_mode)
  542.             printf(">>>%s: Empty name in NAME\r\n", Name);
  543.         return;
  544.     }
  545.     if (cnt > sizeof(savename) - 1) {
  546.         if (auth_debug_mode)
  547.             printf(">>>%s: Name in NAME (%d) exceeds %d length\r\n",
  548.                     Name, cnt, sizeof(savename)-1);
  549.         return;
  550.     }
  551.     memmove((void *)savename, (void *)data, cnt);
  552.     savename[cnt] = '\0';    /* Null terminate */
  553.     if (auth_debug_mode)
  554.         printf(">>>%s: Got NAME [%s]\r\n", Name, savename);
  555.     auth_encrypt_user(savename);
  556. }
  557.  
  558.     int
  559. auth_sendname(cp, len)
  560.     unsigned char *cp;
  561.     int len;
  562. {
  563.     static unsigned char str_request[256+6]
  564.             = { IAC, SB, TELOPT_AUTHENTICATION, TELQUAL_NAME, };
  565.     register unsigned char *e = str_request + 4;
  566.     register unsigned char *ee = &str_request[sizeof(str_request)-2];
  567.  
  568.     while (--len >= 0) {
  569.         if ((*e++ = *cp++) == IAC)
  570.             *e++ = IAC;
  571.         if (e >= ee)
  572.             return(0);
  573.     }
  574.     *e++ = IAC;
  575.     *e++ = SE;
  576.     net_write(str_request, e - str_request);
  577.     printsub('>', &str_request[2], e - &str_request[2]);
  578.     return(1);
  579. }
  580.  
  581.     void
  582. auth_finished(ap, result)
  583.     Authenticator *ap;
  584.     int result;
  585. {
  586.     if (!(authenticated = ap))
  587.         authenticated = &NoAuth;
  588.     validuser = result;
  589. }
  590.  
  591.     /* ARGSUSED */
  592.     static void
  593. auth_intr(sig)
  594.     int sig;
  595. {
  596.     auth_finished(0, AUTH_REJECT);
  597. }
  598.  
  599.     int
  600. auth_wait(name)
  601.     char *name;
  602. {
  603.     if (auth_debug_mode)
  604.         printf(">>>%s: in auth_wait.\r\n", Name);
  605.  
  606.     if (Server && !authenticating)
  607.         return(0);
  608.  
  609.     (void) signal(SIGALRM, auth_intr);
  610.     alarm(30);
  611.     while (!authenticated)
  612.         if (telnet_spin())
  613.             break;
  614.     alarm(0);
  615.     (void) signal(SIGALRM, SIG_DFL);
  616.  
  617.     /*
  618.      * Now check to see if the user is valid or not
  619.      */
  620.     if (!authenticated || authenticated == &NoAuth)
  621.         return(AUTH_REJECT);
  622.  
  623.     if (validuser == AUTH_VALID)
  624.         validuser = AUTH_USER;
  625.  
  626.     if (authenticated->status)
  627.         validuser = (*authenticated->status)(authenticated,
  628.                              name, validuser);
  629.     return(validuser);
  630. }
  631.  
  632.     void
  633. auth_debug(mode)
  634.     int mode;
  635. {
  636.     auth_debug_mode = mode;
  637. }
  638.  
  639.     void
  640. auth_printsub(data, cnt, buf, buflen)
  641.     unsigned char *data, *buf;
  642.     int cnt, buflen;
  643. {
  644.     Authenticator *ap;
  645.  
  646.     if ((ap = findauthenticator(data[1], data[2])) && ap->printsub)
  647.         (*ap->printsub)(data, cnt, buf, buflen);
  648.     else
  649.         auth_gen_printsub(data, cnt, buf, buflen);
  650. }
  651.  
  652.     void
  653. auth_gen_printsub(data, cnt, buf, buflen)
  654.     unsigned char *data, *buf;
  655.     int cnt, buflen;
  656. {
  657.     register unsigned char *cp;
  658.     unsigned char tbuf[16];
  659.  
  660.     cnt -= 3;
  661.     data += 3;
  662.     buf[buflen-1] = '\0';
  663.     buf[buflen-2] = '*';
  664.     buflen -= 2;
  665.     for (; cnt > 0; cnt--, data++) {
  666.         sprintf((char *)tbuf, " %d", *data);
  667.         for (cp = tbuf; *cp && buflen > 0; --buflen)
  668.             *buf++ = *cp++;
  669.         if (buflen <= 0)
  670.             return;
  671.     }
  672.     *buf = '\0';
  673. }
  674. #endif
  675.